/*
 * @Author: 李石
 * @Date: 2024-05-17 10:09:28
 * @LastEditors: lishi
 * @LastEditTime: 2024-05-22 19:29:30
 * @Description: 
 * Copyright (c) 2024 by ${lishi0105@163.com}, All Rights Reserved. 
 */
#include <fstream>
#include <unistd.h>
#include <fstream>
#include <sstream>
#include "logger.hpp"
#include "file_util.h"
#include "utilspp.hpp"
#include "SysParams.hpp"

std::unique_ptr<SysParams> SysParams::instance = nullptr;
std::mutex SysParams::SingleMutex;

SysParams* SysParams::getInstance(const std::string& config_path) 
{
    std::lock_guard<std::mutex> lock(SingleMutex);
    if (instance == nullptr) {
        std::string config_path_ = config_path;
        instance = std::unique_ptr<SysParams>(
            new SysParams(config_path_));
    }
    return instance.get();
}

SysParams::SysParams(const std::string& config_path_) :
    m_config_path_(config_path_), streams_info_(nullptr)
{
    
}

SysParams::~SysParams() {}

bool SysParams::setToDefaultConf()
{
    std::ofstream o(m_config_path_);
    if (!o.is_open()) {
        LOGE("Unable to write open file :{}", m_config_path_);
        return false;
    }
    streams_info_ = StreamsInfoDto::CreateShared();
    streams_info_->count = 0;
    streams_info_->streams_by_app_and_stream.clear();
    o << streams_info_->ToString() << std::endl;
    return true;
}

bool SysParams::load()
{
    std::lock_guard<std::mutex> lock(mutex_);
    std::ifstream file(m_config_path_);
    if (!file.is_open()) {
        LOGE("Unable to open file :{}", m_config_path_);
        return setToDefaultConf();
    }
    std::ostringstream ss;
    ss << file.rdbuf();
    std::string fileContent = ss.str();
    LOGI("Reading JSON from file :{} success :[{}]", m_config_path_, fileContent);
    streams_info_ = StreamsInfoDto::Fetch(fileContent);
    if (!streams_info_) {
        LOGE("Unable to parse JSON file :{}", m_config_path_);
        file.close();
        return setToDefaultConf();
    }
    for(auto it = streams_info_->streams_by_app_and_stream.begin(); it != streams_info_->streams_by_app_and_stream.end(); ++it) {
        auto stream_ptr = it->second;
        if (!stream_ptr) {
            LOGE("problem with stream :{}", it->first);
            continue;
        }
        stream_ptr->alive = false;
        stream_ptr->codec_id = 0;
        stream_ptr->play = UtilsPP::formatString("http://localhost:8080/%s/%s.live.ts", stream_ptr->app.c_str(), stream_ptr->stream.c_str());
    }
    file.close();
    return true;
}

bool SysParams::save() const
{
    std::lock_guard<std::mutex> lock(mutex_);
    std::ofstream o(m_config_path_);
    if (!o.is_open()) {
        LOGE("Unable to open file :{}", m_config_path_);
        o.close();
        return false;
    }
    o << streams_info_->ToString() << std::endl;
    o.close();
    LOGI("Writing JSON to file :{} success", m_config_path_);
    return true;
}

bool SysParams::insert_stream(const std::string &app, const std::string &stream, const std::string &url, int rtp_type, std::string &err)
{
    std::lock_guard<std::mutex> lock(mutex_);
    err = "success";
    if(streams_info_->streams_by_app_and_stream.size() >= 16){
        err = "stream count is limited to 16";
        LOGE(err);
        return false;
    }
    if(!UtilsPP::isValidMediaUrl(url)){
        err = "only http:// https:// rtsp:// rtmp:// is supported";
        LOGE(err);
        return false;
    }
    if(!UtilsPP::isValidString(app) || !UtilsPP::isValidString(stream)){
        err = "app and stream must be valid string";
        LOGE(err);
        return false;
    }
    std::string key = app + ":" + stream;
    auto it = streams_info_->streams_by_app_and_stream.find(key);
    if (it != streams_info_->streams_by_app_and_stream.end()) {
        err = "Unable to insert stream, stream already exists";
        return false;
    }
    
    auto stream_ptr = StreamDto::CreateShared();
    stream_ptr->url = url;
    stream_ptr->app = app;
    stream_ptr->stream = stream;
    stream_ptr->rtp_type = rtp_type;
    stream_ptr->play = "";
    stream_ptr->alive = false;
    stream_ptr->codec_id = 0;
    streams_info_->streams_by_app_and_stream[key] = stream_ptr;
    streams_info_->count = streams_info_->streams_by_app_and_stream.size();
    return true;
}

bool SysParams::remove_stream(const std::string &app, const std::string &stream, std::string &err)
{
    std::lock_guard<std::mutex> lock(mutex_);
    auto key = app + ":" + stream;
    err = "success";
    auto it = streams_info_->streams_by_app_and_stream.find(key);
    if (it != streams_info_->streams_by_app_and_stream.end()) {
        streams_info_->streams_by_app_and_stream.erase(it);
        streams_info_->count = streams_info_->streams_by_app_and_stream.size();
        return true;
    }
    err = "not found";
    return false;
}

bool SysParams::update_stream(const std::string &app, const std::string &stream, const int codec, bool alive, const std::string &play)
{
    std::lock_guard<std::mutex> lock(mutex_);
    auto key = app + ":" + stream;
    auto it = streams_info_->streams_by_app_and_stream.find(key);
    if (it != streams_info_->streams_by_app_and_stream.end()) {
        it->second->codec_id = codec;
        it->second->alive = alive;
        it->second->play = play;
        return true;
    }
    return false;
}

StreamReportDto::Ptr SysParams::get_stream(const std::string &app, const std::string &stream, std::string &err) const
{
    std::lock_guard<std::mutex> lock(mutex_);
    auto key = app + ":" + stream;
    auto it = streams_info_->streams_by_app_and_stream.find(key);
    if(it == streams_info_->streams_by_app_and_stream.end()) {
        err = "not found";
        return nullptr;
    }
    err = "success";
    StreamReportDto::Ptr dto = StreamReportDto::CreateShared();
    dto->app = it->second->app;
    dto->stream = it->second->stream;
    dto->codec_id = it->second->codec_id;
    dto->alive = it->second->alive;
    dto->play = it->second->play;
    dto->url = it->second->url;
    dto->rtp_type = it->second->rtp_type;
    return dto;
}

StreamListDto::Ptr SysParams::get_stream(std::string &err) const
{
    std::lock_guard<std::mutex> lock(mutex_);
    err = "success";
    if (streams_info_->streams_by_app_and_stream.empty()) {
        err = "not found";
        return nullptr;
    }
    err = "success";
    StreamListDto::Ptr list_ = StreamListDto::CreateShared();
    for(auto it = streams_info_->streams_by_app_and_stream.begin(); it != streams_info_->streams_by_app_and_stream.end(); ++it) {
        StreamReportDto::Ptr dto = StreamReportDto::CreateShared();
        dto->app = it->second->app;
        dto->stream = it->second->stream;
        dto->codec_id = it->second->codec_id;
        dto->alive = it->second->alive;
        dto->play = it->second->play;
        dto->url = it->second->url;
        dto->rtp_type = it->second->rtp_type;
        list_->streams.push_back(dto);
    }
    list_->count = list_->streams.size();
    return list_;
}